// Sky.cpp: implementation of the Sky class.
//
//////////////////////////////////////////////////////////////////////

#include "Sky.h"
#include <math.h>

static sgVec4 black             = { 0.0f, 0.0f, 0.0f, 1.0f } ;
static sgVec4 white             = { 1.0f, 1.0f, 1.0f, 1.0f } ;

static sgVec4 base_sky_color    = { 0.39f, 0.50f, 0.74f, 1.0f } ;
static sgVec4 base_fog_color    = { 0.84f, 0.87f, 1.00f, 1.0f } ;

static sgVec4 base_ambient      = { 0.2f, 0.2f, 0.2f, 1.0f } ;
static sgVec4 base_diffuse      = { 1.0f, 1.0f, 1.0f, 1.0f } ;
static sgVec4 base_specular     = { 1.0f, 1.0f, 1.0f, 1.0f } ;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Sky::Sky()
{
	sky = NULL;
	nstars = NUMBER_OF_STARS;
	nplanets = 0;
	star_data = NULL;
	planet_data = NULL;

	fogEnabled = 1;

	curr_body = 0;
	curr_cloud = 0;

	bodyNameList[0] = "Sun";
	bodyNameList[1] = "Moon";
	bodyNameList[2] = NULL;

	cloudNameList[0] = "Cloud 0";
	cloudNameList[1] = "Cloud 1";
	cloudNameList[2] = "Cloud 2";
	cloudNameList[3] = NULL;

	m_log01 = -log( 0.01 );
	sqrt_m_log01 = sqrt( m_log01 );

	  /* Build the scene graph */

  star_data = new sgdVec3[nstars] ;
  for ( int i = 0; i < nstars; i++ )
  {
    star_data[i][0] = ssgaRandom() * SGD_PI ;
	star_data[i][1] = ssgaRandom() * SGD_PI ;
	star_data[i][2] = ssgaRandom() ;
  }

  /* sky */
  sky       =  new ssgaSky ;
  sky       -> build ( HOR_RADIUS, VER_RADIUS, nplanets, planet_data, NUMBER_OF_STARS , star_data);
  /* sun */
  bodies[0] =  sky -> addBody (
                  NULL,                 // body texture
				  "./Sky/data/halo.rgba",     // halo texture
				  SUN_SIZE,                 // size
				  SUN_DISTANCE,                // distance
				  true );               // is sun - dome painted based on this
  bodies[0] -> setDeclination  ( SUN_DECLINATION*SGD_DEGREES_TO_RADIANS );
  bodies[0] -> setRightAscension (SUN_RIGHT_ASCENSION*SGD_DEGREES_TO_RADIANS );
  /* moon */
  bodies[1] =  sky -> addBody (
                  "./Sky/data/moon.rgba",     // body texture
				  NULL,                 // halo texture
				  MOON_SIZE,                 // size
				  MOON_DISTANCE );              // distance
  bodies[1] -> setDeclination  ( MOON_DECLINATION*SGD_DEGREES_TO_RADIANS );
  bodies[1] -> setRightAscension (MOON_RIGHT_ASCENSION*SGD_DEGREES_TO_RADIANS );
  /* clouds */
  clouds[0] =  sky -> addCloud (
				 "./Sky/data/scattered.rgba", // texture
                 CLOUDS_SPAN,                 // span
				 CLOUDS_ELEVATION_1,          // elevation,
				 CLOUDS_THICKNESS,            // thickness
				 CLOUDS_TRANSITION );         // transition
  clouds[0] -> setSpeed ( 50 ) ;
  clouds[0] -> setDirection ( 45 ) ;
/*  clouds[1] =  sky -> addCloud (
				 "./Sky/data/scattered.rgba", // texture
                 CLOUDS_SPAN,                 // span
				 CLOUDS_ELEVATION_2,          // elevation,
				 CLOUDS_THICKNESS,            // thickness
				 CLOUDS_TRANSITION );         // transition
  clouds[1] -> setSpeed ( 20 ) ;
  clouds[1] -> setDirection ( 30 ) ;
  clouds[2] =  sky -> addCloud (
				 "./Sky/data/scattered.rgba", // texture
                 CLOUDS_SPAN,                 // span
				 CLOUDS_ELEVATION_3,          // elevation,
				 CLOUDS_THICKNESS,            // thickness
				 CLOUDS_TRANSITION );         // transition
  clouds[2] -> setSpeed ( 5 ) ;
  clouds[2] -> setDirection ( 20 ) ;*/


#ifndef CLOUDY_DAY
    // disable clouds:
  clouds[0]->disable();
  clouds[1]->disable();
  clouds[2]->disable();
#endif
	
  this->sunTransform = new ssgTransform;

  bodies[0] -> getPosition ( & solpos );
  ssgGetLight ( 0 ) -> setPosition ( solpos.xyz ) ;
  sunTransform->setTransform(&solpos);  
}

Sky::~Sky()
{

}

void Sky::updateSky(float dt)
{
  #ifdef SUN_MOVING
  bodies[0] -> setDeclination ( bodies[0] -> getDeclination() - 0.01*SGD_DEGREES_TO_RADIANS );
  #endif

  /* update sky */
  sky -> repositionFlat ( campos.xyz, 0, dt );
  sky -> modifyVisibility ( campos.xyz[SG_Z], dt );

  sol_angle = bodies[0]->getAngle();
  sky_brightness = (1.0 + cos(sol_angle))/2.0; // 0.0 - 1.0
  scene_brightness = pow(sky_brightness,0.5);
  
  /* set sky color */
  sky_color[0] = base_sky_color[0] * sky_brightness;
  sky_color[1] = base_sky_color[1] * sky_brightness;
  sky_color[2] = base_sky_color[2] * sky_brightness;
  sky_color[3] = base_sky_color[3];

  /* set cloud and fog color */
  cloud_color[0] = fog_color[0] = base_fog_color[0] * sky_brightness;
  cloud_color[1] = fog_color[1] = base_fog_color[1] * sky_brightness;
  cloud_color[2] = fog_color[2] = base_fog_color[2] * sky_brightness;
  cloud_color[3] = fog_color[3] = base_fog_color[3];

  /* repaint the sky */
 // printf("sol_angle: %f\n",sol_angle);
 // printf("campos: %f %f %f %f %f %f\n",campos.xyz[SG_X],campos.xyz[SG_X],campos.xyz[SG_Y],campos.xyz[HEADING],campos.hpr[PITCH],campos.hpr[ROLL]);
  //printf("%d %d\n",nstars,star_data);
  sky -> repaint ( sky_color, fog_color, cloud_color, sol_angle, nplanets, planet_data, nstars, star_data);

  /* set light source */
  /* REMEMBER! the sunTransform is directly connected with the scenegraph
     and is passed to the LensFlare, so that it is updated without other
	 calls!!! */
  bodies[0] -> getPosition ( & solpos );
  ssgGetLight ( 0 ) -> setPosition ( solpos.xyz ) ;
  sunTransform->setTransform(&solpos);

  scene_ambient[0] = base_ambient[0] * (float)scene_brightness;
  scene_ambient[1] = base_ambient[1] * (float)scene_brightness;
  scene_ambient[2] = base_ambient[2] * (float)scene_brightness;
  scene_ambient[3] = 1.0;

  scene_diffuse[0] = base_diffuse[0] * (float)scene_brightness;
  scene_diffuse[1] = base_diffuse[1] * (float)scene_brightness;
  scene_diffuse[2] = base_diffuse[2] * (float)scene_brightness;
  scene_diffuse[3] = 1.0;

  scene_specular[0] = base_specular[0] * (float)scene_brightness;
  scene_specular[1] = base_specular[1] * (float)scene_brightness;
  scene_specular[2] = base_specular[2] * (float)scene_brightness;
  scene_specular[3] = 1.0;
  // GL_LIGHT_MODEL_AMBIENT has a default non-zero value so if
  // we only update GL_AMBIENT for our lights we will never get
  // a completely dark scene.  So, we set GL_LIGHT_MODEL_AMBIENT
  // explicitely to black.
  glLightModelfv( GL_LIGHT_MODEL_AMBIENT, black );
  ssgGetLight( 0 ) -> setColour( GL_AMBIENT, scene_ambient );
  ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, scene_diffuse );
  ssgGetLight( 0 ) -> setColour( GL_SPECULAR, scene_specular );
}

ssgaCelestialBody* Sky::getBody(int i)
{
	return bodies[i];
}

ssgaSky* Sky::getSky()
{
	return sky;
}

int Sky::getNplanets()
{
	return nplanets;
}

int Sky::getNstars()
{
	return nstars;
}

GLfloat Sky::getFogDensity()
{
	return sqrt_m_log01 / sky->getVisibility();
}

double Sky::getSkyBrightness()
{
	return sky_brightness;
}

double Sky::getSceneBrightness()
{
	return scene_brightness;
}

GLfloat* Sky::getFogColor()
{
	return fog_color;
}

void Sky::setCameraPosition(sgCoord campos)
{
	sgCopyCoord(&(this->campos),&(campos));
}


void Sky::preDraw()
{
  /* Adjust fog based on visibility (when in clouds) */
if(this->fogEnabled)
	{
	GLfloat fog_exp2_density = (float)sqrt_m_log01 / this->sky->getVisibility();
	glEnable( GL_FOG );
	glFogf  ( GL_FOG_DENSITY, fog_exp2_density);
	glFogi  ( GL_FOG_MODE,    GL_EXP2 );
	glFogfv ( GL_FOG_COLOR,   fog_color );
	glClearColor(fog_color[0],fog_color[1],fog_color[2],fog_color[3]);
	}
else
	{
	glDisable(GL_FOG);
	glClearColor(fog_color[0],fog_color[1],fog_color[2],fog_color[3]);
	}

  /* pre-Draw Sky */

   // some OpenGL set up for enabling alpha blending
   glEnable ( GL_DEPTH_TEST ) ;
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glAlphaFunc(GL_ALWAYS,0.0f);
   glEnable(GL_ALPHA_TEST);
   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

   glEnable( GL_AMBIENT_AND_DIFFUSE);

  // we need a white diffuse light for the phase of the moon
  ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, white );
	
  this->sky->preDraw();

  // return to the desired diffuse color
  ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, scene_diffuse );
}

void Sky::postDraw()
{
	
   // some OpenGL set up for enabling alpha blending
   glEnable ( GL_DEPTH_TEST ) ;
   glEnable(GL_TEXTURE_2D);
   glEnable(GL_BLEND);
   glAlphaFunc(GL_ALWAYS,0.0f);
   glEnable(GL_ALPHA_TEST);
   glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

   glEnable( GL_AMBIENT_AND_DIFFUSE);

   // draw clouds (blended!)
   this->sky->postDraw(this->campos.xyz[SG_Z]);
}

void Sky::getSunPosition(sgCoord* sunpos)
{
      bodies[0]->getPosition(sunpos);
}

ssgTransform* Sky::getTransform(void)
{
	return this->sunTransform;
}

void Sky::enableFog()
{
	this->fogEnabled = 1;
}

void Sky::disableFog()
{
	this->fogEnabled = 0;
}

